#lang errortrace racket
(require "ast.rkt")

(define (safe-/ x y)
  (cond [(= y 0) #f]
        [else (/ x y)]))

(define (r:eval-builtin sym)
  (cond [(equal? sym '+) +]
        [(equal? sym '*) *]
        [(equal? sym '-) -]
        [(equal? sym '/) safe-/]
        [else #f]))

(define (handle-err o cont)
  (cond [(false? o) #f]
        [else (cont o)]))

(define-syntax do
  (syntax-rules (<-)
    ; Only one monadic-op, return it
    [(_ mexp) mexp]
    ; A binding operation
    [(_ var <- mexp rest ...) (handle-err mexp (lambda (var) (do rest ...)))]
    ; No binding operator, just ignore the return value
    [(_ mexp rest ...)        (handle-err mexp (lambda (_) (do rest ...)))]))

(define/contract (r:eval-exp exp)
  (-> r:expression? (or/c number? #f))
  (cond
    [(r:number? exp) (r:number-value exp)]
    [(r:variable? exp) (r:eval-builtin (r:variable-name exp))]
    [(r:apply? exp)
    (do
      func <- (r:eval-exp (r:apply-func exp))
      arg1 <- (r:eval-exp (first (r:apply-args exp)))
      arg2 <- (r:eval-exp (second (r:apply-args exp)))
      (func arg1 arg2))]
    ;  (handle-err (r:eval-exp (r:apply-func exp))
    ;   (lambda (func)
    ;     (handle-err (r:eval-exp (first (r:apply-args exp)))
    ;       (lambda (lhs)
    ;         (handle-err (r:eval-exp (second (r:apply-args exp)))
    ;           (lambda (rhs)
    ;             (func lhs rhs))))))
    ;   )]
    ;  (cond [(false? func) #f]
    ;        [else
    ;         (define lhs (r:eval-exp (first (r:apply-args exp))))
    ;         (cond [(false? lhs) #f]
    ;               [else
    ;                 (define rhs (r:eval-exp (second (r:apply-args exp))))
    ;                 (cond [(false? rhs) #f]
    ;                       [else (func lhs rhs)])])])]
    [else (error "Unknown expression:" exp)]))

; Exercise 1
; (r:eval-exp 10) ; Not OK

(require rackunit)
(check-equal?
  (r:eval-exp (r:number 10)) ; OK
  10)




; Exercise 2
; Expression:
; (/ 1 0)
; Not OK

(check-equal?
(r:eval-exp
  (r:apply
    (r:variable '/)
    (list (r:number 1) (r:number 0))))
  #f)

; Exercise 3
; Expression: (+ (/ 1 0) 2)
; (+ #f 2)

(check-equal?
  (r:eval-exp
    (r:apply (r:variable '+)
      (list
        (r:number 2)
        (r:apply
          (r:variable '/)
          (list (r:number 1) (r:number 0)))
        )))
  #f)

(check-equal?
 (r:eval-exp
  (r:apply
    (r:variable 'modulo)
    (list (r:number 1) (r:number 0))))
  #f
)












;